/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package managedbean.MrpSop;

import merlion.mrp.ppm.pojo.LongHolder;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import javax.ejb.EJB;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import merlion.common.util.Consts;
import merlion.mrp.ppm.entity.ArchivedIndivSOP;
import merlion.mrp.ppm.entity.SOP;
import merlion.mrp.ppm.session.SalesOptPlanningSessionLocal;

/**
 *
 * @author Zhang Ying
 */
@ManagedBean
@ViewScoped
public class MrpSopEditManagerBean {

    @EJB
    private SalesOptPlanningSessionLocal mrpSopSession;
    // counters
    private ArrayList<Integer> counterForMonth; // 3
    private ArrayList<Integer> counterForProduct; // 4
    // edit page
    private ArrayList<String> monthLabels;
    private ArrayList<ArrayList<Long>> seasonSalesForecasts;
    private ArrayList<Long> previousInventories;
    private ArrayList<Integer> workingDayList;
    private ArrayList<ArrayList<LongHolder>> inputtedUplannedDemands; // 4*3
    private ArrayList<ArrayList<LongHolder>> inputtedInventories;// 4*3
    // for selecting sop for edition
    private SOP selectedSOP;
    private Timestamp currentTime;

    /** Creates a new instance of MrpSopEditManagerBean */
    public MrpSopEditManagerBean() {
        // init counters
        counterForMonth = new ArrayList<Integer>();
        for (int i = 0; i < 3; i++) {
            counterForMonth.add(new Integer(i));
        }
        counterForProduct = new ArrayList<Integer>();
        for (int i = 0; i < 4; i++) {
            counterForProduct.add(new Integer(i));
        }
        // init current time
        Calendar calendar = Calendar.getInstance();
        Date date = calendar.getTime();
        currentTime = new Timestamp(date.getTime());
    }

    public SOP getSelectedSOP() {
        if (selectedSOP == null) {
            SOP temp = (SOP) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get(MrpSopViewManagerBean.EDIT_SOP);
            if (temp != null) {
                selectedSOP = temp;
                changePeriodRelatedInfo(selectedSOP.getStartPeriodMonth());
                // init long holders
                inputtedInventories = new ArrayList<ArrayList<LongHolder>>();
                inputtedUplannedDemands = new ArrayList<ArrayList<LongHolder>>();
                for (int i = 0; i < 4; i++) {
                    ArrayList<LongHolder> temp1 = new ArrayList<LongHolder>();
                    ArrayList<LongHolder> temp2 = new ArrayList<LongHolder>();
                    for (int j = 0; j < 3; j++) {
                        LongHolder lh1 = new LongHolder();
                        LongHolder lh2 = new LongHolder();
                        lh1.setL(selectedSOP.returnProductIndivSOP(i).getInventoryList().get(j).getLongValue());
                        lh2.setL(selectedSOP.returnProductIndivSOP(i).getUnplannedDemandList().get(j).getLongValue());
                        temp1.add(lh1);
                        temp2.add(lh2);
                    }
                    inputtedInventories.add(temp1);
                    inputtedUplannedDemands.add(temp2);
                }
            }
        }
        return selectedSOP;
    }

    public void setSelectedSOP(SOP selectedSOP) {
        this.selectedSOP = selectedSOP;
    }

    public ArrayList<Integer> getCounterForMonth() {
        return counterForMonth;
    }

    public void setCounterForMonth(ArrayList<Integer> counterForMonth) {
        this.counterForMonth = counterForMonth;
    }

    public ArrayList<Integer> getCounterForProduct() {
        return counterForProduct;
    }

    public void setCounterForProduct(ArrayList<Integer> counterForProduct) {
        this.counterForProduct = counterForProduct;
    }

    public ArrayList<String> getMonthLabels() {
        getSelectedSOP();
        return monthLabels;
    }

    public void setMonthLabels(ArrayList<String> monthLabels) {
        this.monthLabels = monthLabels;
    }

    public ArrayList<Long> getPreviousInventories() {
        if (previousInventories == null) {
            previousInventories = mrpSopSession.getInventoryOfPrevSeasonLastMonth(getSelectedSOP().getStartPeriodYear(), getSelectedSOP().getStartPeriodMonth());
//            previousInventories = new ArrayList<Long>();
//            previousInventories.add(new Long(200));
//            previousInventories.add(new Long(150));
//            previousInventories.add(new Long(50));
//            previousInventories.add(new Long(100));
        }
        return previousInventories;
    }

    public void setPreviousInventories(ArrayList<Long> previousInventories) {
        this.previousInventories = previousInventories;
    }

    public ArrayList<ArrayList<Long>> getSeasonSalesForecasts() {
        if (seasonSalesForecasts == null) {
            seasonSalesForecasts = new ArrayList<ArrayList<Long>>();
            for (int i = 0; i < 4; i++) {
                ArrayList<Long> sf = new ArrayList<Long>();
                for (int j = 0; j < 3; j++) {
                    sf.add(getSelectedSOP().returnProductIndivSOP(i).getSalesforcastValues().get(j).getLongValue());
                }
                seasonSalesForecasts.add(sf);
            }
        }
        return seasonSalesForecasts;
    }

    public void setSeasonSalesForecasts(ArrayList<ArrayList<Long>> seasonSalesForecasts) {
        this.seasonSalesForecasts = seasonSalesForecasts;
    }

    public ArrayList<Integer> getWorkingDayList() {
        if (workingDayList == null) {
            workingDayList = mrpSopSession.getWorkingDaysList(getSelectedSOP().getStartPeriodYear(), getSelectedSOP().getStartPeriodMonth());
        }
        return workingDayList;
    }

    public void setWorkingDayList(ArrayList<Integer> workingDayList) {
        this.workingDayList = workingDayList;
    }

    public ArrayList<ArrayList<LongHolder>> getInputtedInventories() {
        return inputtedInventories;
    }

    public void setInputtedInventories(ArrayList<ArrayList<LongHolder>> inputtedInventories) {
        this.inputtedInventories = inputtedInventories;
    }

    public ArrayList<ArrayList<LongHolder>> getInputtedUplannedDemands() {
        return inputtedUplannedDemands;
    }

    public void setInputtedUplannedDemands(ArrayList<ArrayList<LongHolder>> inputtedUplannedDemands) {
        this.inputtedUplannedDemands = inputtedUplannedDemands;
    }

    private void changePeriodRelatedInfo(Integer month) {
        monthLabels = new ArrayList<String>();
        if (month.equals(1)) {
            monthLabels.add(Consts.DEC);
            monthLabels.add(Consts.JAN);
            monthLabels.add(Consts.FEB);
            monthLabels.add(Consts.MAR);
        } else if (month.equals(4)) {
            monthLabels.add(Consts.MAR);
            monthLabels.add(Consts.APR);
            monthLabels.add(Consts.MAY);
            monthLabels.add(Consts.JUN);
        } else if (month.equals(7)) {
            monthLabels.add(Consts.JUN);
            monthLabels.add(Consts.JUL);
            monthLabels.add(Consts.AUG);
            monthLabels.add(Consts.SEP);
        } else if (month.equals(10)) {
            monthLabels.add(Consts.SEP);
            monthLabels.add(Consts.OCT);
            monthLabels.add(Consts.NOV);
            monthLabels.add(Consts.DEC);
        }
    }

    public Long roundToLong(Double d) {
        return Math.round(d);
    }

    private String getRedirectPath(String path) {
        return path + "?faces-redirect=true";
    }

    public String returnProductAlpha(Integer i) {
        if (i.intValue() == 0) {
            return Consts.ProductType.BAR_A.getText();
        } else if (i.intValue() == 1) {
            return Consts.ProductType.BAR_B.getText();
        } else if (i.intValue() == 2) {
            return Consts.ProductType.BAR_C.getText();
        } else if (i.intValue() == 3) {
            return Consts.ProductType.BAR_D.getText();
        } else {
            return "";
        }
    }

    public Long returnTotalDemand(Integer monthCounter, Integer productCounter) {
        Long sfVal = seasonSalesForecasts.get(productCounter).get(monthCounter);
        Long upVal = selectedSOP.returnProductIndivSOP(productCounter).getUnplannedDemandList().get(monthCounter).getLongValue();
        return sfVal + upVal;
    }

    public Long returnProducionPlan(Integer monthCounter, Integer productCounter) {
        return selectedSOP.returnProductIndivSOP(productCounter).getProductionPlanList().get(monthCounter).getLongValue();
    }

    public Long returnCapacityWithNormalShift(Integer workingDay) {
        return (long) Consts.NORMAL_SHIFT_PER_DAY * workingDay;
    }

    public Long returnCapacityWithOTShift(Integer workingDay) {
        return (long) Consts.OVERTIME_SHIFT_PER_DAY * workingDay;
    }

    public Long returnTotalPlannedProductionHrs(Integer monthCounter) {
        Long sum = new Long(0);
        for (int i = 0; i < 4; i++) {
            Long prodPlan = selectedSOP.returnProductIndivSOP(i).getProductionPlanList().get(monthCounter).getLongValue();
            //sum += roundToLong(1.0 * prodPlan / Consts.CASES_PER_HOUR);
            if (i == 0 || i == 1) {
                sum += roundToLong(1.0 * prodPlan / Consts.CASES_PER_HOUR_CLASSIC);
            } else {
                sum += roundToLong(1.0 * prodPlan / Consts.CASES_PER_HOUR_PREMIUM);
            }
        }
        return sum;
    }

    public Double returnUtilizationWNormalShift(Integer monthCounter) {
        Long sum = new Long(0);
        for (int i = 0; i < 4; i++) {
            Long prodPlan = selectedSOP.returnProductIndivSOP(i).getProductionPlanList().get(monthCounter).getLongValue();
            //sum += roundToLong(1.0 * prodPlan / Consts.CASES_PER_HOUR);
            if (i == 0 || i == 1) {
                sum += roundToLong(1.0 * prodPlan / Consts.CASES_PER_HOUR_CLASSIC);
            } else {
                sum += roundToLong(1.0 * prodPlan / Consts.CASES_PER_HOUR_PREMIUM);
            }
        }
        Long cap = (long) Consts.NORMAL_SHIFT_PER_DAY * workingDayList.get(monthCounter);
        return 1.0 * sum / cap;
    }

    public Double returnUtilizatinWOTShift(Integer monthCounter) {
        Long sum = new Long(0);
        for (int i = 0; i < 4; i++) {
            Long prodPlan = selectedSOP.returnProductIndivSOP(i).getProductionPlanList().get(monthCounter).getLongValue();
            //sum += roundToLong(1.0 * prodPlan / Consts.CASES_PER_HOUR);
            if (i == 0 || i == 1) {
                sum += roundToLong(1.0 * prodPlan / Consts.CASES_PER_HOUR_CLASSIC);
            } else {
                sum += roundToLong(1.0 * prodPlan / Consts.CASES_PER_HOUR_PREMIUM);
            }
        }
        Long cap = (long) Consts.OVERTIME_SHIFT_PER_DAY * workingDayList.get(monthCounter);
        return (1.0 * sum / cap);
    }

    public void refreshToLatestSF(ActionEvent event) {
        seasonSalesForecasts = mrpSopSession.getSeasonSalesForecastDataForUpdate(getSelectedSOP().getStartPeriodYear(), getSelectedSOP().getStartPeriodMonth());
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 3; j++){
                selectedSOP.returnProductIndivSOP(i).getSalesforcastValues().get(j).setLongValue(seasonSalesForecasts.get(i).get(j));
            }
        }
        calculateSOP(event);
    }

    public void calculateSOP(ActionEvent event) {
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 3; j++) {
                if (inputtedInventories.get(i).get(j).getL() < 0) {
                    FacesContext.getCurrentInstance().addMessage(null,
                            new FacesMessage(FacesMessage.SEVERITY_ERROR, "Unsuccessful",
                            "All inputted inventory field should not be negative"));
                    return;
                }
                if (inputtedUplannedDemands.get(i).get(j).getL() < 0) {
                    FacesContext.getCurrentInstance().addMessage(null,
                            new FacesMessage(FacesMessage.SEVERITY_ERROR, "Unsuccessful",
                            "All inputted unplanned demand field should not be negative."));
                    return;
                }
            }
        }
        // put the inputted values into selectedSOP's list
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 3; j++) {
                Long sfVal = seasonSalesForecasts.get(i).get(j);
                Long unplanned = inputtedUplannedDemands.get(i).get(j).getL();
                Long totalDemand = sfVal + unplanned;
                Long inv = inputtedInventories.get(i).get(j).getL();
                Long prevInventory;
                if (j == 0) {
                    prevInventory = previousInventories.get(i);
                } else {
                    prevInventory = selectedSOP.returnProductIndivSOP(i).getInventoryList().get(j - 1).getLongValue();
                }
                selectedSOP.returnProductIndivSOP(i).getUnplannedDemandList().get(j).setLongValue(unplanned);
                selectedSOP.returnProductIndivSOP(i).getInventoryList().get(j).setLongValue(inv);
                selectedSOP.returnProductIndivSOP(i).getProductionPlanList().get(j).setLongValue(totalDemand + inv - prevInventory);
            }
        }
    }

    public String confirmSOP() {
        // validations: check the utilization
        for (int m = 0; m < 3; m++) {
            Long sum = new Long(0);
            for (int i = 0; i < 4; i++) {
                Long prodPlan = selectedSOP.returnProductIndivSOP(i).getProductionPlanList().get(m).getLongValue();
                //sum += roundToLong(1.0 * prodPlan / Consts.CASES_PER_HOUR);
                if (i == 0 || i == 1) {
                    sum += roundToLong(1.0 * prodPlan / Consts.CASES_PER_HOUR_CLASSIC);
                } else {
                    sum += roundToLong(1.0 * prodPlan / Consts.CASES_PER_HOUR_PREMIUM);
                }
            }
            Long cap = (long) Consts.NORMAL_SHIFT_PER_DAY * workingDayList.get(m);
            if (1.0 * sum / cap > 0.9) {
                FacesContext.getCurrentInstance().addMessage(null,
                        new FacesMessage(FacesMessage.SEVERITY_WARN, "Unsuccessful",
                        "Some of your utilization is over 90%. We recommend you to change it."));
                return null;
            }
        }
        // validation 2: check whether have negative production plan
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 3; j++) {
                if (selectedSOP.returnProductIndivSOP(i).getProductionPlanList().get(j).getLongValue() < 0) {
                    FacesContext.getCurrentInstance().addMessage(null,
                            new FacesMessage(FacesMessage.SEVERITY_WARN, "Unsuccessful",
                            "Your production plan field cannot be negative."));
                    return null;
                }
                if (selectedSOP.returnProductIndivSOP(i).getInventoryList().get(j).getLongValue() < 0){
                    FacesContext.getCurrentInstance().addMessage(null, 
                            new FacesMessage(FacesMessage.SEVERITY_ERROR, "Unsuccessful",
                                    "Sorry your inventory number should not be negative."));
                }
            }
        }
        // add the archive inside
        ArrayList<ArchivedIndivSOP> asops = (ArrayList<ArchivedIndivSOP>) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get(MrpSopViewManagerBean.ARCHIVED_INDIVS);
        for (int i = 0; i < 4; i++) {
            selectedSOP.returnProductIndivSOP(i).getArchivedIndivSops().add(asops.get(i));
        }
        if (mrpSopSession.persistMrpSop(selectedSOP)) {
            FacesContext.getCurrentInstance().getExternalContext().getSessionMap().remove(MrpSopViewManagerBean.EDIT_SOP);
            FacesContext.getCurrentInstance().getExternalContext().getSessionMap().remove(MrpSopViewManagerBean.ARCHIVED_INDIVS);
            return getRedirectPath(Consts.CREATE_MRP_SOP_PATH + "Confirm");
        } else {
            FacesContext.getCurrentInstance().addMessage(null,
                    new FacesMessage(FacesMessage.SEVERITY_ERROR, "Unsuccessful",
                    "not able to persist into the database"));
            return null;
        }
    }

    // rendering
    public boolean renderInventory(LongHolder item, Integer productCounter) {
        boolean render = false;

        int index = inputtedUplannedDemands.get(productCounter).indexOf(item);
        int pointedMonth = index + selectedSOP.getStartPeriodMonth();
        int pointedYear = selectedSOP.getStartPeriodYear().intValue();

        Calendar c1 = Calendar.getInstance();
        Calendar c2 = Calendar.getInstance();

        //  for testing
        //c2.set(2011, 10, 24); // Nov
        System.out.println("testing for time" + Consts.sdfMonth.format(c2.getTime()));
        c1.set(pointedYear, pointedMonth-1, 1,0,0,0); // pointed planned time

        long millis1 = c1.getTimeInMillis();
        long millis2 = c2.getTimeInMillis(); // current time

        long d = (millis1 - millis2) / (24 * 60 * 60 * 1000);

        if (d > 21) {
            render = true;
        }

        return render;
    }

    public boolean renderUnplannedDemand(LongHolder item, Integer productCounter) {
        boolean render = false;

        int index = inputtedUplannedDemands.get(productCounter).indexOf(item);
        int pointedMonth = index + selectedSOP.getStartPeriodMonth();
        int pointedYear = selectedSOP.getStartPeriodYear().intValue();

        Calendar c1 = Calendar.getInstance();
        Calendar c2 = Calendar.getInstance();

        //  for testing
//        c2.set(2011, 10, 24);
        c1.set(pointedYear, pointedMonth-1, 1,0,0,0); // pointed planned time

        long millis1 = c1.getTimeInMillis();
        long millis2 = c2.getTimeInMillis(); // current time

        long d = (millis1 - millis2) / (24 * 60 * 60 * 1000);

        if (d > 21) {
            render = true;
        }
//        System.out.println("days are " + d);
        return render;
    }
}
